﻿module BABYLON.Internals {
    export class AndOrNotEvaluator {
        public static Eval(query: string, evaluateCallback: (val) => boolean): boolean {
            if (!query.match(/\([^\(\)]*\)/g)) {
                query = AndOrNotEvaluator._HandleParenthesisContent(query, evaluateCallback);
            }
            else {
                query = query.replace(/\([^\(\)]*\)/g, r => {
                    // remove parenthesis
                    r = r.slice(1, r.length - 1);
                    return AndOrNotEvaluator._HandleParenthesisContent(r, evaluateCallback);
                });
            }

            if (query === "true") {
                return true;
            }

            if (query === "false") {
                return false;
            }

            return AndOrNotEvaluator.Eval(query, evaluateCallback);
        }

        private static _HandleParenthesisContent(parenthesisContent: string, evaluateCallback: (val) => boolean): string {
            evaluateCallback = evaluateCallback || ((r) => {
                return r === "true" ? true : false;
            });

            var result;
            var or = parenthesisContent.split("||");

            for (var i in or) {
                var ori = AndOrNotEvaluator._SimplifyNegation(or[i].trim());
                var and = ori.split("&&");

                if (and.length > 1) {
                    for (var j = 0; j < and.length; ++j) {
                        var andj = AndOrNotEvaluator._SimplifyNegation(and[j].trim());
                        if (andj !== "true" && andj !== "false") {
                            if (andj[0] === "!") {
                                result = !evaluateCallback(andj.substring(1));
                            }
                            else {
                                result = evaluateCallback(andj);
                            }
                        }
                        else {
                            result = andj === "true" ? true : false;
                        }
                        if (!result) { // no need to continue since 'false && ... && ...' will always return false
                            ori = "false";
                            break;
                        }
                    }
                }

                if (result || ori === "true") { // no need to continue since 'true || ... || ...' will always return true
                    result = true;
                    break;
                }

                // result equals false (or undefined)

                if (ori !== "true" && ori !== "false") {
                    if (ori[0] === "!") {
                        result = !evaluateCallback(ori.substring(1));
                    }
                    else {
                        result = evaluateCallback(ori);
                    }
                }
                else {
                    result = ori === "true" ? true : false;
                }
            }

            // the whole parenthesis scope is replaced by 'true' or 'false'
            return result ? "true" : "false";
        }

        private static _SimplifyNegation(booleanString: string): string {
            booleanString = booleanString.replace(/^[\s!]+/, r => {
                // remove whitespaces
                r = r.replace(/[\s]/g, () => "");
                return r.length % 2 ? "!" : "";
            });

            booleanString = booleanString.trim();

            if (booleanString === "!true") {
                booleanString = "false";
            }
            else if (booleanString === "!false") {
                booleanString = "true";
            }

            return booleanString;
        }
    }
} 